
#include <stdlib.h>
#include <string.h>

#include "util.h"
#include "input.h"

/* Defines */
#define DEF_CALLBACK_SIZE 32
#define DEF_MULTIKEY_LIMIT 32
#define KEY_COUNT 256
#define MAX_MULTIKEY_BINDS 32

/* Types */
typedef void (*KeyCallback)(KeyEvent k, int action);
typedef void (*MultiKeyCallback)(int * k, int key_count, int action);

DEFINE_VECTOR(KeyCallback, CBVector);
DEFINE_VECTOR(MultiKeyCallback, MultiCBVector);
DEFINE_VECTOR(int, KeyVector);

/* Data */
static CBVector key_callbacks;
static MultiCBVector multikey_callbacks;
static CBVector mouse_callbacks;
static CBVector action_callbacks;

static KeyVector multikey_list;

static int key_binds[KEY_COUNT] = {0};

static struct 
{
	int * key_list;
	int count;
	int action;
}multikey_binds[MAX_MULTIKEY_BINDS];
static int multikey_count;

static int multikey_toggle;
static int multikey_toggled;
/* Code */

void init_input(void)
{
	VEC_INIT(KeyCallback, DEF_CALLBACK_SIZE, key_callbacks);
	VEC_INIT(KeyCallback, DEF_CALLBACK_SIZE, mouse_callbacks);
	VEC_INIT(KeyCallback, DEF_CALLBACK_SIZE, action_callbacks);
	VEC_INIT(KeyCallback, DEF_CALLBACK_SIZE, key_callbacks);
	VEC_INIT(KeyCallback, DEF_CALLBACK_SIZE, mouse_callbacks);
	VEC_INIT(KeyCallback, DEF_CALLBACK_SIZE, action_callbacks);
	VEC_INIT(MultiKeyCallback, DEF_CALLBACK_SIZE, multikey_callbacks);
	VEC_INIT(int, DEF_MULTIKEY_LIMIT, multikey_list);
}

void free_input(void)
{
	int i;
	for(i = 0; i < multikey_count; ++i)
		free(multikey_binds[i].key_list);

	VEC_FREE(KeyCallback, key_callbacks);
	VEC_FREE(KeyCallback, mouse_callbacks);
	VEC_FREE(KeyCallback, action_callbacks);
	VEC_FREE(KeyCallback, key_callbacks);
	VEC_FREE(KeyCallback, mouse_callbacks);
	VEC_FREE(KeyCallback, action_callbacks);
	VEC_FREE(MultiKeyCallback, multikey_callbacks);
	VEC_FREE(int, multikey_list);

	

}

/* Press a toggle key then enter in multiple keys, NOTE: they have to be all held down
 * this function may be retarded 
 * Press toggle key to reset
 */
static void handle_multikey(KeyEvent k) 
{
	int i, j, i2;
	/* If multikey_toggle == 0 this shoudl always fail unless sdl returns a
	 * Unknown key which will be 0 
	 */
	if(k.event_type == K_KEYDOWN && k.key.k == multikey_toggle) 
	{
		multikey_toggled = 1;
		multikey_list.count = 0;
		return;
	}
	else if (k.event_type == K_KEYUP && k.key.k == multikey_toggle){
		multikey_toggled = 0;
		return;
	}

	if(multikey_toggled && k.event_type == K_KEYDOWN){
		VEC_PUSH(int, multikey_list, k.key.k);

		for(i = 0; i < multikey_count; ++i)
		{
			/* Compare entered keys against list of multi key binds */
			if(multikey_list.count == multikey_binds[i].count){
				for(j = 0;; ++j){
					if(multikey_list.elements[j] != multikey_binds[i].key_list[j])
						break;
				
					/* I should probably invest some time in removing this much nesting */
					if(j == multikey_list.count - 1){
						for(i2 = 0; i2 < multikey_callbacks.count; ++i2)
							multikey_callbacks.elements[i2](multikey_list.elements, multikey_list.count, multikey_binds[i].action);
					}
				}
			}
		}
	}

}

void keyboard_event(KeyEvent k)
{
	int i, action;
	handle_multikey(k);
	for(i = 0; i < key_callbacks.count; ++i){
		if(k.event_type == K_KEYDOWN || k.event_type == K_KEYUP)
			action = key_binds[k.key.k];
		else action = k.key.c;

		key_callbacks.elements[i](k, action);
	}
}

void set_multikey_toggle(unsigned int vkey)
{
	multikey_toggle = vkey;
}

void bind_action(unsigned int vkey, int action)
{
	if(vkey < KEY_COUNT)
		key_binds[vkey] = action;
}

void bind_multikey_action(int * vkeys, int key_count, int action)
{
	int * list;
	if(vkeys == NULL || key_count < 1)return;
	if(multikey_count < MAX_MULTIKEY_BINDS)
	{
		list = (int*)malloc(key_count * sizeof(int));
		memcpy(list, vkeys, key_count * sizeof(int));
		multikey_binds[multikey_count].count	= key_count;
		multikey_binds[multikey_count].action	= action;
		multikey_binds[multikey_count].key_list = list;
		++multikey_count;
	}
}

void add_key_callback(KeyCallback callback)
{
	VEC_PUSH(KeyCallback, key_callbacks, callback);
}

void add_multikey_callback(MultiKeyCallback callback)
{
	VEC_PUSH(MultiKeyCallback, multikey_callbacks, callback);
}

void remove_key_callback(KeyCallback callback)
{
	VEC_REMOVE(KeyCallback, key_callbacks, callback);
}

void remove_multikey_callback(MultiKeyCallback callback)
{
	int i;
	for(i = 0; i < multikey_count; ++i)
	{
		if(multikey_callbacks.elements[i] == callback)
			VEC_REMOVE(MultiKeyCallback, multikey_callbacks, callback);
	}
}
